using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NetCorePal.Extensions.CodeAnalysis.SourceGenerators;

[Generator]
public class ControllerMethodMetadataGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var methodDeclarations = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: (node, _) => node is MethodDeclarationSyntax,
                transform: (ctx, _) => ctx.Node as MethodDeclarationSyntax)
            .Where(m => m != null);

        var compilationAndMethods = context.CompilationProvider.Combine(methodDeclarations.Collect());

        context.RegisterSourceOutput(compilationAndMethods, (spc, source) =>
        {
            var (compilation, methodNodes) = source;
            var controllerMetas = new List<(string ControllerType, string MethodName, List<string> CommandTypes)>();

            foreach (var method in methodNodes)
            {
                if (method == null) continue;
                var semanticModel = compilation.GetSemanticModel(method.SyntaxTree);
                if (semanticModel == null) continue;
                var methodSymbol = semanticModel.GetDeclaredSymbol(method) as IMethodSymbol;
                if (methodSymbol == null) continue;
                var containingType = methodSymbol.ContainingType;
                if (containingType == null || containingType.DeclaredAccessibility != Accessibility.Public) continue;
                // 使用扩展方法判断是否为 Controller
                if (!containingType.IsController()) continue;

                // 查找参数和方法体内所有命令类型
                var commandTypes = new HashSet<string>();
                // 1. 参数中的命令类型
                foreach (var param in methodSymbol.Parameters)
                {
                    if (param.Type is INamedTypeSymbol namedType && GeneratorExtensions.IsCommand(namedType))
                    {
                        commandTypes.Add(namedType.ToDisplayString());
                    }
                }
                // 2. 方法体内的命令发送调用
                if (method.Body != null)
                {
                    foreach (var invocation in method.Body.DescendantNodes().OfType<InvocationExpressionSyntax>())
                    {
                        var invokedSymbol = semanticModel.GetSymbolInfo(invocation).Symbol as IMethodSymbol;
                        if (invokedSymbol == null) continue;
                        var name = invokedSymbol.Name.ToLower();
                        if (name.Contains("send") || name.Contains("publish") || name.Contains("execute") || name.Contains("dispatch"))
                        {
                            // 尝试获取命令类型参数
                            if (invocation.ArgumentList.Arguments.Count > 0)
                            {
                                var arg = invocation.ArgumentList.Arguments[0];
                                var argType = semanticModel.GetTypeInfo(arg.Expression).Type;
                                if (argType is INamedTypeSymbol namedType2 && GeneratorExtensions.IsCommand(namedType2))
                                {
                                    commandTypes.Add(namedType2.ToDisplayString());
                                }
                            }
                        }
                    }
                }
                // 无论是否发出命令，都记录Controller元数据
                controllerMetas.Add((containingType.ToDisplayString(), methodSymbol.Name, commandTypes.ToList()));
            }

            if (controllerMetas.Count > 0)
            {
                var sb = new StringBuilder();
                sb.AppendLine("// <auto-generated/>\nusing System;\nusing NetCorePal.Extensions.CodeAnalysis.Attributes;");
                foreach (var (controllerType, methodName, commandTypes) in controllerMetas)
                {
                    string commandsLiteral;
                    if (commandTypes.Count == 0)
                    {
                        commandsLiteral = string.Empty;
                    }
                    else
                    {
                        commandsLiteral = string.Join(", ", commandTypes.Select(c => $"\"{c}\""));
                    }
                    sb.AppendLine(commandTypes.Count == 0
                        ? $"[assembly: ControllerMethodMetadataAttribute(\"{controllerType}\", \"{methodName}\")]\n"
                        : $"[assembly: ControllerMethodMetadataAttribute(\"{controllerType}\", \"{methodName}\", {commandsLiteral})]\n");
                }
                spc.AddSource("ControllerMethodMetadata.g.cs", sb.ToString());
            }
        });
    }
}
